#include "led.h"
/* 接口说明
大致工作原理:
    (1) A B C D 四条地址线通过两片TC7262组成了行选择器,让某一行的电源全部接通
    (2) 二进制 0bDCBA 代表具体的某一行
    (3) TC5020提供16个恒流驱动接口,根据R1或G1与CLK LAT的配合,将数据移位到移位寄存器
        决定导通某一列.
    (4) 由此,任何一个LED均可以导通,但同一时刻只能导通一行.
    (5) 因为有两个TC7262可以选中16行中的某一行,所以,一屏显示需要分别接通16行,才能显示
        完整,所以是16扫.

地址线:
    A B C D
        (1) 地址线信号经74HC245缓冲后, 给了两片TC7262译码成16行
        (2) 当地址线60-70ms没有变动时, 芯片保护,输出关闭
    LAT
        (1) LAT 信号经74HC245送给了TC5020
        (2) 任何时候可以移位给TC5020设置数据
        (3) 当LAT由低向高跳变时,数据被锁存

    OE
        (1) OE 信号占了74HC245的两路
        (2) 一路送给了下一块板
        (2) 另一路送给了列选择器的３脚OE，OE=0时,输出关闭,显示关闭
 */
const GPIO_Init_Param_Typedef GPIO_HUB08[20] =
    {
        // 右边
        {GPIOB, {LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // A
        {GPIOB, {LL_GPIO_PIN_2, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // B
        {GPIOB, {LL_GPIO_PIN_11, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // C
        {GPIOB, {LL_GPIO_PIN_13, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // D
        {GPIOB, {LL_GPIO_PIN_15, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // G1
        {GPIOB, {LL_GPIO_PIN_4, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // G2
        {GPIOB, {LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // LAT
        {GPIOB, {LL_GPIO_PIN_8, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // CLK
                                                                                                                // 左边
        {GPIOB, {LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
        {GPIOB, {LL_GPIO_PIN_10, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // GND
        {GPIOB, {LL_GPIO_PIN_12, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // GND
        {GPIOB, {LL_GPIO_PIN_14, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0}, // OE
        {GPIOB, {LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // R1
        {GPIOB, {LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // R2
        {GPIOB, {LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
        {GPIOB, {LL_GPIO_PIN_9, LL_GPIO_MODE_OUTPUT, LL_GPIO_SPEED_FREQ_HIGH, LL_GPIO_OUTPUT_PUSHPULL, 0}, 0},  // GND
        {0, {0, 0, 0, 0, 0}, 0}};

/* 缓冲区大小 */
uint16_t font[16 * BUF_SIZE] = {0};

// HUB08 双色接口 需要11根控制线.这里初始化这些IO口
void led_init()
{
    GPIO_Init((GPIO_Init_Param_Typedef *)GPIO_HUB08);
}

/* 行选择 */
void set_current_row(uint8_t row)
{
    D = row >> 3;
    C = row >> 2;
    B = row >> 1;
    A = row >> 0;
}

/* 从缓冲区读出某一行数据移入屏幕 */
void shift_data_to_led(uint8_t row, uint8_t n_bit)
{
    uint16_t *cur = font + row * BUF_SIZE;
    uint16_t tmp;
    // 先移入64bit,这样就满屏显示了
    for (uint8_t n = 0; n < (n_bit >> 4); n++)
    {
        tmp = *cur;
        for (uint8_t c = 0; c < 16; c++)
        {
            G1 = tmp;
            tmp >>= 1;
            CLK = 0;
            CLK = 1;
        }
        cur++;
    }
    tmp = *cur;
    // 再移入64bit之后的,这样可以模拟左移效果
    for (uint8_t c = 0; c < n_bit % 16; c++)
    {
        G1 = tmp;
        tmp >>= 1;
        CLK = 0;
        CLK = 1;
    }
}

/* 读字体点阵到缓冲区 */
void spread_data_to_display_buf(uint16_t gb2312)
{
    uint16_t tmp[16];
    zk_read_16x16_font((uint8_t *)tmp, gb2312); // 读字库
    zk_V2H_16x16_font((uint8_t *)tmp);          // 竖转横,选取恰当的字库就不需要这样了,这个过程相当耗时
    for (uint8_t i = 0; i < 16; i++)            // 显存中的点阵向前移一个位置
    {
        for (uint8_t j = 0; j < BUF_SIZE - 1; j++)
        {
            font[j + i * BUF_SIZE] = font[1 + j + i * BUF_SIZE];
        }
    }
    for (uint8_t i = 0; i < 16; i++) // 写入新读取的
    {
        font[i * BUF_SIZE + BUF_SIZE - 1] = tmp[i];
    }
}

/*
    1  最快20us可刷新一行， 16行，1320us，也就是一毫秒可刷新一屏
    2  因为是测试函数, 这里没去考虑半角全角的转换.全是全角字符
*/
void display_text(uint8_t *text)
{
    uint16_t *cur = (uint16_t *)text;
    uint32_t last_count, cur_count;
    tim1_count = last_count = 0;
    LL_TIM_EnableCounter(TIM1);
    while (1)
    {
        cur_count = tim1_count;
        // 读一个字到缓冲区尾部
        if (tim1_count % 16 == 0 && last_count != tim1_count)
        {
            last_count = tim1_count;
            spread_data_to_display_buf(*cur++);
            if (*cur == 0x00)
            {
                return;
            }
        }
        // 移位, 锁存一定要在 DisplayOFF 之后
        for (uint8_t r = 0; r < 16; r++)
        {
            DisplayON;
            shift_data_to_led(r, 64 + cur_count % 16);
            DisplayOFF;
            Latch;
            set_current_row(r);
        }
    }
}